#pragma once

//
// Math
//
class Math
{
public:
	// Constants.
	enum {ANGLE_SHIFT 	= 2};		// Bits to right-shift to get lookup value.
	enum {ANGLE_BITS	= 14};		// Number of valid bits in angles.
	enum {NUM_ANGLES 	= 16384}; 	// Number of angles that are in lookup table.
	enum {ANGLE_MASK    =  (((1<<ANGLE_BITS)-1)<<(16-ANGLE_BITS))};

public:
	Math();
	~Math();

	
	float SinTab(int i) const;
	float CosTab(int i) const;
	float SinFloat(float f) const;
	float CosFloat(float f) const;
	
	static int Trunc(float v);
	static int Round(float f);
	static float InvSqrt(float f);
	static float UnitRadom();
	static float RangeRadom(float InMin, float InMax);

private:
	float  mTable[NUM_ANGLES];
};

extern Math GMath;

inline float Math::SinTab(int i) const
{
	return mTable[(i>>ANGLE_SHIFT) & (NUM_ANGLES-1)];
}

inline float Math::CosTab( int i ) const
{
	return mTable[((i+16384)>>ANGLE_SHIFT) & (NUM_ANGLES-1)];
}

inline float Math::SinFloat(float f) const
{
	return SinTab(Trunc((f*65536.f)/(2.f*PI)));
}

inline float Math::CosFloat(float f) const
{
	return CosTab(Trunc((f*65536.f)/(2.f*PI)));
}

inline int Math::Trunc(float v)
{
	__asm cvttss2si eax,[v];
}

inline int Math::Round( float F )
{
	__asm cvtss2si eax,[F]
}

inline float Math::InvSqrt(float f)
{
	const float fThree = 3.0f;
	const float fOneHalf = 0.5f;
	float temp;

	__asm
	{
		movss	xmm1,[f]
		rsqrtss	xmm0,xmm1			// 1/sqrt estimate (12 bits)

		// Newton-Raphson iteration (X1 = 0.5*X0*(3-(Y*X0)*X0))
		movss	xmm3,[fThree]
		movss	xmm2,xmm0
		mulss	xmm0,xmm1			// Y*X0
		mulss	xmm0,xmm2			// Y*X0*X0
		mulss	xmm2,[fOneHalf]		// 0.5*X0
		subss	xmm3,xmm0			// 3-Y*X0*X0
		mulss	xmm3,xmm2			// 0.5*X0*(3-Y*X0*X0)
		movss	[temp],xmm3
	}

	return temp;
}

inline float Math::UnitRadom()
{
	return rand() / (FLOAT)RAND_MAX;
}

inline float Math::RangeRadom(float InMin, float InMax)
{
	return InMin + (InMax - InMin) * UnitRadom();
}
